#include "StdAfx.h"
#include "listen_agent.h"

listen_agent::listen_agent( const std::string& port, 
							Concurrency::unbounded_buffer<SOCKET>& accepted_sockets ) 
	: _addr_results(nullptr)
	, _accepted_sockets(accepted_sockets)
	, _addrptr(nullptr)
	, _fn_result(FALSE)
	, _port(port)
	, _was_init(FALSE)
	, _listen_socket(INVALID_SOCKET)
{
	ZeroMemory(&_addr_hints, sizeof(_addr_hints));
}

BOOL listen_agent::init()
{
#define cleanup_and_return_false	 { cleanup(); return FALSE; }

	if (TRUE == _was_init)
		return TRUE;

	_addr_hints.ai_family = AF_INET;
	_addr_hints.ai_socktype = SOCK_STREAM;
	_addr_hints.ai_protocol = IPPROTO_TCP;
	_addr_hints.ai_flags = AI_PASSIVE;

	_fn_result = getaddrinfo(NULL, _port.c_str(), &_addr_hints, &_addr_results);
	if (FALSE == check_sock_fn_result_and(NULL))
		cleanup_and_return_false;

	if (nullptr == _addr_results)
	{
		ATLTRACE2(_T("Unable to resolve interface %s\n"), _port.c_str());
		cleanup_and_return_false;
	}

	_addrptr = _addr_results;
	_listen_socket = socket(_addrptr->ai_family, _addrptr->ai_socktype, _addrptr->ai_protocol);

	_fn_result = static_cast<INT>(_listen_socket);
	if (FALSE == check_sock_fn_result_not(INVALID_SOCKET))
		cleanup_and_return_false;

	_fn_result = bind(_listen_socket, _addrptr->ai_addr, static_cast<INT>(_addrptr->ai_addrlen));
	if (FALSE == check_sock_fn_result_not(SOCKET_ERROR))
		cleanup_and_return_false;

	_fn_result = listen(_listen_socket, 10);
	if (FALSE == check_sock_fn_result_not(SOCKET_ERROR))
		cleanup_and_return_false;

	_was_init = TRUE;
	return _was_init;
}


listen_agent::~listen_agent(void)
{
}

void listen_agent::run()
{
	if (FALSE == init())
	{
		cancel();
		return;
	}

	SOCKADDR_STORAGE	sock_address;
	INT					sock_address_len = sizeof(sock_address);
	CHAR				servstr[NI_MAXSERV];
	CHAR				hoststr[NI_MAXHOST];
	SOCKET				client_socket = INVALID_SOCKET;

	ZeroMemory(&sock_address, sizeof(sock_address));
	ZeroMemory(&servstr, sizeof(servstr));
	ZeroMemory(&hoststr, sizeof(hoststr));

	while (1)
	{
		client_socket = accept(_listen_socket, (SOCKADDR*)&sock_address, &sock_address_len);

		_fn_result = static_cast<INT>(client_socket);
		if (FALSE == check_sock_fn_result_not(INVALID_SOCKET))
		{
			ATLTRACE2(_T("accept error\n"));
			break;
		}

		_fn_result = getnameinfo((SOCKADDR*)&sock_address,
									sock_address_len,
									hoststr,
									_countof(hoststr),
									servstr,
									_countof(servstr),
									NI_NUMERICHOST | NI_NUMERICSERV);
		if (FALSE == check_sock_fn_result_and(NULL))
		{
			ATLTRACE2("getnameinfo error\n");
			break;
		}

		ATLTRACE2("accepted connevtion from host %s and port %s\n", hoststr, servstr);

		Concurrency::send(_accepted_sockets, client_socket);
	}

	cancel();
}

BOOL listen_agent::check_sock_fn_result_and(const INT& valid_val)
{
	if (valid_val == _fn_result)
		return TRUE;

	ATLTRACE2(_T("error result: %d\n"), _fn_result);
	ATLTRACE2(_T("socket error: %d\n"), WSAGetLastError());
	return FALSE;
}

BOOL listen_agent::check_sock_fn_result_not( const INT& invalid_val )
{
	if (invalid_val != _fn_result)
		return TRUE;

	ATLTRACE2(_T("error result: %d\n"), _fn_result);
	ATLTRACE2(_T("socket error: %d\n"), WSAGetLastError());
	return FALSE;
}

BOOL listen_agent::cleanup()
{
	if (nullptr != _addr_results)
	{
		freeaddrinfo(_addr_results);
		_addr_results = nullptr;
	}

	if (INVALID_SOCKET != _listen_socket)
	{
		closesocket(_listen_socket);
		_listen_socket = INVALID_SOCKET;
	}
	return TRUE;
}

void listen_agent::cancel()
{
	ATLTRACE2("listen_agent cancel()\n");
	done();
}

void listen_agent::done()
{
	cleanup();
	agent::done();
}
